using System;
using System.Collections.Generic;
using FrameIdentityReq.Network;

namespace FrameIdentityReq.Frames
{
    public class BaseFrame : IBytesBuffer
    {
        public static event EventHandler<SocketState> OnRestored;
        public static event EventHandler<SocketState> OnUnknowFrameError;
        public static event EventHandler<SocketState> OnProtocolVersionError;
        public static event EventHandler<SocketState> OnFactoryCodeError;
        public static event EventHandler<SocketState> OnFrameTypeError;


        public IList<byte> Buffer { get; }

        /// <summary>
        /// 协议版本. 
        /// 帧首字节高 4 bit. 当前为 4.
        /// 0-3保留,
        /// 4～15用于区分不同厂家.
        /// </summary>
        public IIntField ProtocolVersion;

        /// <summary>
        /// 厂家代码. 4 bit. ProtocolVersion 之后. 当前为 4.
        /// </summary>
        public IntField FactoryCode;

        /// <summary>
        /// 帧类型. 5 bit. FactoryCode 之后.
        /// </summary>
        public IFrameTypeField FrameType;

        /// <summary>
        /// 信息长度. 11 bit. FrameType 之后.
        /// </summary>
        public IIntField InfoLength;

        // <summary>
        /// CRC 16位检验和. 16 bit. 帧结尾.
        /// </summary>
        public IUInt16Field CRC;

        public BaseFrame()
        {
            Buffer = new List<byte>(new byte[AddFields()]);

            ProtocolVersion.Value = (int)BaseFrameDataEnum.ProtocolVersion;
            FactoryCode.Value = (int)BaseFrameDataEnum.FactoryCode;
            FrameType.Value = FrameTypeEnum.NULL;
            InfoLength.Value = 0;
        }

        public BaseFrame(IList<byte> buffer)
        {
            int length = AddFields();
            Buffer = (buffer != null && length <= buffer.Count) ? buffer : null;
        }

        public static void Restore(SocketState state)
        {
            IList<byte> buffer = state.Buffer;
            BaseFrame frame = new BaseFrame(buffer);

            if (frame.Buffer == null)
            {
                if (OnUnknowFrameError != null)
                    OnUnknowFrameError(frame, state);
                return;
            }

            frame = frame.ToRealFrame();

            if (frame == null)
            {
                if (OnFrameTypeError != null)
                    OnFrameTypeError(frame, state);
                return;
            }

            if (frame.Buffer == null || frame.CRC.Value != frame.CalculationCRC())
            {
                if (OnUnknowFrameError != null)
                    OnUnknowFrameError(frame, state);
                return;
            }

            if (frame.ProtocolVersion.Value != 4)
            {
                if (OnProtocolVersionError != null)
                    OnProtocolVersionError(frame, state);
                return;
            }

            if (frame.FactoryCode.Value != 4)
            {
                if (OnFactoryCodeError != null)
                    OnFactoryCodeError(frame, state);
                return;
            }

            frame.RaiseRestored(state);
        }

        protected virtual void RaiseRestored(SocketState state)
        {
            if (OnRestored != null)
                OnRestored(this, state);
        }

        protected int AddFields()
        {
            ProtocolVersion = new IntField(this, 0, 4, "ProtocolVersion");
            FactoryCode = new IntField(ProtocolVersion, 4, "FactoryCode");
            FrameType = new FrameTypeField(FactoryCode, "FrameType");
            InfoLength = new IntField(FrameType, 11, "InfoLength");

            IField field = AddBodyFields(InfoLength);

            CRC = new UInt16Field(field, "CRC");
            return BitsField.CeilingByteCount(CRC.Index + CRC.Count);
        }

        protected virtual IField AddBodyFields(IField field) { return field; }

        public UInt16 CalculationCRC()
        {
            return CalculationCRC(Buffer, 0, BitsField.CeilingByteCount(CRC.Index));
        }

        public static UInt16 CalculationCRC(IList<byte> buffer, int index, int count)
        {
            UInt16 usCrc = 0xFFFF;

            for (int i = index; count > 0; i++, count--)
            {
                usCrc ^= (UInt16)buffer[i];
                for (int j = 0; j < 8; j++)
                {
                    int poly = (usCrc & 1) == 1 ? 0xA001 : 0;
                    usCrc >>= 1;
                    usCrc ^= (UInt16)poly;
                }
            }

            return usCrc;
        }
    }
}